package com.paradogs.framework.core.config;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.paradogs.framework.common.pojo.mp.ItemMap;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.*;
import org.apache.poi.ss.formula.functions.T;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jmx.export.annotation.ManagedOperationParameter;

import javax.sql.DataSource;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: yumi
 * @date: 2023/11/20  11:17
 * @Description: TODO
 */
/**
 * mybatis-plus 扫描要精确到 dao 或 mapper，否则自动装配会有问题（dao(service)找不到正确 mapper）
 * 可能原因是扫描重叠了，dao(service)也被当成 mapper 自动生成代理实现类了
 * !!! 虽然是微服务，但是所有业务的 dao 都放在一起了，!! 方便单人开发 !!，如果需要正常的微服务操作，可以取消这里的注册，自行在对应服务中编写 dao 和 pojo
 */
@AutoConfiguration
@ComponentScan(basePackages = {"com.paradogs.framework.common.dao.*"}) // 要精确到 dao
@MapperScan("com.paradogs.framework.common.mapper") // 不能加 *
public class MybatisPlusConfiguration {

    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 插入时填充 createTime
                this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 插入时填充 updateTime
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 更新时填充 updateTime
            }
        };
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // 设置数据源
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        // 设置 JSON 对象转换器
        MybatisConfiguration configuration = new MybatisConfiguration();
        TypeHandlerRegistry registry = configuration.getTypeHandlerRegistry();

        registry.register(JsonObjectTypeHandler.class);
        registry.register(MapTypeHandler.class);

        factoryBean.setConfiguration(configuration);
        return factoryBean.getObject();
    }

    public static abstract class PRTypeHandler<T> implements TypeHandler<T> {

        public abstract T DB2Java(String db);
        public abstract String Java2DB(T java);

        @Override
        public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
            ps.setString(i, Java2DB(parameter));
        }
        @Override
        public T getResult(ResultSet rs, String columnName) throws SQLException {
            return DB2Java(rs.getString(columnName));
        }
        @Override
        public T getResult(ResultSet rs, int columnIndex) throws SQLException {
            return DB2Java(rs.getString(columnIndex));
        }
        @Override
        public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
            return DB2Java(cs.getString(columnIndex));
        }
    }

    /**
     * 源码位置 TypeHandlerRegistry.register()
     * 好坑：代码执行找不到对应转换器，看了源码发现内部自己根据某些自定义注解（@MappedTypes、@MappedJdbcTypes）解析了，
     * 需要 javaType 和 jdbcType
     */
    @MappedTypes(Object.class)
    @MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.LONGVARCHAR})
    public static class JsonObjectTypeHandler extends PRTypeHandler<Object> {
        @Override
        public Object DB2Java(String db) {
            return JSON.parse(db);
        }
        @Override
        public String Java2DB(Object java) {
            return JSON.toJSONString(java);
        }
    }


    /**
     * 大坑 !!! Mybatis-Plus 的 javaType 转换类型只会找实际的类型，不会找其父类等，
     * 比如 Map 类，你如果想转换所有 Map 类型，你只能把所有 Map 的实现类写到 @MappedTypes 里去
     *     !!! 类型转换：读取的时候会找到父类的类型，写入的时候只能找到实际的类型，建议还是 @MappedTypes 还是全写了
     */
    @MappedTypes({Map.class, LinkedHashMap.class, HashMap.class, ConcurrentHashMap.class})
    @MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.LONGVARCHAR})
    public static class MapTypeHandler extends PRTypeHandler<Map> {
        @Override
        public Map DB2Java(String db) {
            Map map = JSON.parseObject(db, Map.class);
            return map;
        }
        @Override
        public String Java2DB(Map java) {
            String s = JSON.toJSONString(java);
            return s;
        }
    }



}
